<template>
  <tm-sheet
    :transprent="props.transprent"
    :round="3"
    _class="flex flex-col overflow"
    :padding="props.padding"
    :margin="props.margin"
  >
    <slot></slot>
  </tm-sheet>
</template>

<script lang="ts" setup>
/** 
 * 表单
 * @description 注意，内部需要放置tm-form-item,不限层级，可随意布局。
 * 对以下表单类组件进行字段收集。
 * 	"tm-radio-group","tm-checkbox-box",
    "tm-input","tm-rate","tm-slider",
    "tm-segtab","tm-switch","tm-upload"
 */
import {
  computed,
  PropType,
  provide,
  ref,
  watch,
  Ref,
  toRaw,
  shallowReadonly,
  nextTick,
  isProxy,
  unref,
  readonly,
} from "vue";
import { formItem,validateResultListType } from "./interface";
import tmSheet from "../tm-sheet/tm-sheet.vue";
import { validateFunCall,getObjectVal } from "../tm-form-item/validateFunCall"
/**
 * 事件说明
 * @method submit 提交表单时触发。
 * @method reset 重置表单时触发
 * @method validate 校验表单时触发
 * @method clearValidate 清除校验状态时触发。
 */
const emits = defineEmits<{
  (e: "submit", event: { data: any; validate: boolean }): void;
  /**只要有数据更改就会触发校验并返回相关数据与submit相同 */
  (e: "validate", event: formItem): void;
  (e: "reset"): void;
  (e: "clearValidate"): void;
  (e: "update:modelValue", event: any): void;
}>();

const props = defineProps({
  modelValue: {
    type: Object as PropType<Object>,
    default: () => {
      return {};
    },
    required: true,
  },
  margin: {
    type: Array as PropType<Array<number>>,
    default: () => [32, 24],
  },
  padding: {
    type: Array as PropType<Array<number>>,
    default: () => [16, 0],
  },
  //表单标签是竖还是横排列。
  //vertical,horizontal
  layout: {
    type: String as PropType<"vertical" | "horizontal">,
    default: "horizontal",
  },
  //如果为0表示自动宽度。
  labelWidth: {
    type: Number,
    default: 160,
  },
  //标签对齐方式
  labelAlign: {
    type: String,
    default: "left",
  },
  //显示下划线。
  border: {
    type: Boolean,
    default: true,
  },
  transprent: {
    type: Boolean,
    default: false,
  },
});
const _modelVal = ref(props.modelValue);
//备份，重置时，使用。
const _backModelVal = uni.$tm.u.deepClone(props.modelValue);
// watchEffect(()=>{
// 	_modelVal.value = props.modelValue
// });
//收集的字段。状态。它与_modelVal是有区别的，用户提供的字段，不一定就会在页面中存在，需要与已经渲染的字段进行匹配
const _callBackModelVal: Ref<Array<formItem>> = ref([]);
const tmFormComnameId = "tmFormId";
//允许被推送的组件清单类型.其它的组件不会被收集进行检验。
const safeFormCom = ref([
  "tm-radio-group",
  "tm-checkbox-box",
  "tm-input",
  "tm-rate",
  "tm-slider",
  "tm-segtab",
  "tm-switch",
  "tm-upload",
]);
//需要对子级，响应的方法。
// 这里为了更好的性能不再使用vue2版本中children方式，而是采用了provide方式与父子间传递。
const formFunCallBack = ref("");
const validateResultList = ref<validateResultListType[]>([])
provide(
  "tmFormFun",
  computed(() => formFunCallBack.value)
);
provide(
  "tmFormLabelWidth",
  computed(() => props.labelWidth)
);
provide(
  "tmFormLabelAlign",
  computed(() => props.labelAlign)
);
provide(
  "tmFormLayout",
  computed(() => props.layout)
);
provide(
  "tmFormBorder",
  computed(() => props.border)
);
provide(
  "tmFormTransprent",
  computed(() => props.transprent)
);
provide(
  "formCallFiled",
  computed(() => _modelVal.value)
);
provide(
  "validateResultList",
  computed(() => validateResultList.value)
);
let timid:any = NaN;
let ptimeId:any = NaN
watch(
  () => props.modelValue,
  () => {
    clearTimeout(timid);
    if(formFunCallBack.value == 'validate'){
      timid = setTimeout(function() {
		  const result = validate();
		  validateResultList.value = [...result.result]
	  }, 100);
    }
  },
  { deep: true }
);


function reset() {
  formFunCallBack.value = "reset";
  let dblack = uni.$tm.u.deepClone(_backModelVal);
  emits("update:modelValue", dblack);
  emits("reset");
  _modelVal.value = dblack;
}
function clearValidate() {
  formFunCallBack.value = "clearValidate";
  nextTick(() => {
    emits("clearValidate");
  });
}
function submit() {
  formFunCallBack.value = "validate";
  let isPass = true;
  uni.$tm.u.throttle(
    () => {
	  const result = validate();
	  validateResultList.value = [...result.result]
      emits("submit", { data: toRaw(_modelVal.value), ...result });
	  
    },
    220,
    false
  );
}
//执行表单检验
function validate() {
  formFunCallBack.value = "validate";
  let par = toRaw(_callBackModelVal.value);
  let isPass = true;
  let list:validateResultListType[]= []
  for (let i = 0, len = par.length; i < len; i++) {
	  let item = par[i];
	  let value = getObjectVal(_modelVal.value,item.field)
	  const vallist = validateFunCall(item.rules,value);
	  let rulstVal = {
		  message: '校验通过',
		  validator: true,
	  }
	  for(let j=0;j<vallist.length;j++){
		  if(!vallist[j].validator){
			  isPass = false
			  rulstVal.message = vallist[j]?.message??'校验通过'
			  rulstVal.validator = vallist[j]?.validator??true
			  break;
		  }
	  }
	  list.push({field:item.field,...rulstVal})
  }
  
  return {result:list,isPass,data: toRaw(_modelVal.value),validate:isPass};
}

function pushKey(item: formItem) {
  if (!item.field) return;
  let idsIndex = _callBackModelVal.value.findIndex((el) => el.id == item.id);
  if (idsIndex == -1) {
    _callBackModelVal.value.push(item);
  } else {
    _callBackModelVal.value[idsIndex] = { ...item };
  }
}
function delKey(item: formItem) {
  let idsIndex = _callBackModelVal.value.findIndex((el) => el.id == item.id);
  if (idsIndex > -1) {
    _callBackModelVal.value.splice(idsIndex, 1);
  }
}
function setObjectVal(obj: any, field = "", val: any) {
  if (field == "") return obj;
  var arr = field.split(".");
  while (arr.length > 1) {
    let key = String(arr.shift());
    obj = isProxy(obj[key]) ? toRaw(obj[key]) : obj[key];
  }
  return (obj[arr[0]] = isProxy(val) ? toRaw(val) : val);
}
/**
 * ref函数
 * @method submit 提交表单
 * @method reset 重置表单
 * @method validate 手动校验表单
 * @method clearValidate 清除校验状态
 */
defineExpose({
  reset,
  validate,
  clearValidate,
  submit,
  pushKey,
  delKey,
  tmFormComnameId,
});
</script>

<style></style>
